package de.mnit.schnipsel.swt.editor.basis;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.ExtendedModifyEvent;
import org.eclipse.swt.custom.ExtendedModifyListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import de.mnit.basis.daten.MN;
import de.mnit.basis.daten.struktur.container.Engpass;
import de.mnit.basis.daten.struktur.gruppe.Gruppe2;
import de.mnit.basis.daten.struktur.schluessel.S_SchluesselListe;
import de.mnit.basis.daten.struktur.schluessel.SchluesselListe;
import de.mnit.basis.swt.anw.A_MN_Gruppe;
import de.mnit.basis.swt.element.S_SWT_Komponente;
import de.mnit.basis.swt.element.steuer.rollbar.gruppe.bild.SWT_TextEditor;
import de.mnit.basis.swt.konstant.SWT_SCHRIFT;
import de.mnit.basis.swt.konstant.SWT_SCHRIFTSTIL;
import de.mnit.basis.sys.event.S_Zuhoerer;



/**
 * @author Michael Nitsche
 * 23.05.2009	Erstellt
 *
 * INFO:
 * the new styles, must be in order and non-overlapping
 */
public class TextEditor extends A_MN_Gruppe<TextEditor> {

	public static void main(String[] args) { start(TextEditor.class, 600,400); }


	private static final int[] zeilenHGFarbe = new int[]{240, 240, 255}; //240,240,255 Ok wäre auch noch 235
	private static final String E_GEAENDERT="geaendert";

	private final SWT_TextEditor k_editor=new SWT_TextEditor();
	private final TE_Zurueck zurueck=new TE_Zurueck(t.k_editor);
	private final TE_Format format  =new TE_Format(t.k_editor);
	private final S_SchluesselListe<TE_STYLE,S_StyleKontrolle> kontrolle=SchluesselListe.neu();

	private final Fn_ZeileVerschieben f_verschieben=new Fn_ZeileVerschieben();
	private final Fn_Transfer f_transfer=new Fn_Transfer();
	private final Fn_ZeilenHintergrund f_zeilenHG = new Fn_ZeilenHintergrund();


	// SETZE

	public TextEditor s1_Rollbalken_H() {
		t.k_editor.s1_Rollbalken_H();
		return this;
	}

	public TextEditor s1_Rollbalken_V() {
		t.k_editor.s1_Rollbalken_V();
		return this;
	}

	public void sWordWrap() {
		t.k_editor.swt().setWordWrap(true);
	}

	public void sTextUndFormat(String text, String format) {
		sLeer();
		Gruppe2<String,StyleRange[]> g=KonverterTF.vonDB(text, format, this.format);
		this.k_editor.sText(g.g1());
		if(g.g2().length>0)
			t.k_editor.swt().setStyleRanges(g.g2());
		t.zurueck.neu();
		t.zurueck.plus();
	}

	public void sLeer() {
		t.zurueck.neu();
		for(S_StyleKontrolle k : kontrolle.gObjekte())
			k.setze(false);
		this.k_editor.sLeer();
	}

	// BEFEHL

	public void ausschneiden() {
		this.f_transfer.ausschneiden();
	}

	public void kopieren() {
		this.f_transfer.kopieren();
	}

	public void einfuegen() {
		this.f_transfer.einfuegen();
	}

	public void einfuegen(String text, String format) {
		Gruppe2<String,StyleRange[]> g=KonverterTF.vonDB(text, format, this.format);
		int offset=this.k_editor.swt().getCaretOffset();
		for(StyleRange sr : g.g2())
			sr.start+=offset;

		this.k_editor.swt().insert(g.g1());
		if(g.g2().length>0)
			t.k_editor.swt().replaceStyleRanges(offset, text.length(), g.g2());

		this.k_editor.swt().setCaretOffset(offset+text.length());
		t.zurueck.plus();
	}

	public void zurueck() {
		t.zurueck.zurueck();
	}

	public void wiederherstellen() {
		t.zurueck.vor();
	}

	public void registriereKontrolle(final TE_STYLE style, final S_StyleKontrolle k) {
		kontrolle.plus(style, k);
		k.sEvent(new Listener() {
			public void handleEvent(Event event) {
				format.setze(style);
			}
		});
		format.eKnopfDeaktiv(new S_Zuhoerer<TE_STYLE[]>() {
			public void verarbeite(TE_STYLE[] tsa) {
				for(TE_STYLE ts : tsa)
					if(ts==style)
						k.setze(false);
			}
		});
	}

	// FORMATE

	public void sSchrift(SWT_SCHRIFT schrift, int groesse, SWT_SCHRIFTSTIL... stile) {
		format.sSchrift(schrift, groesse, stile);
	}

	public void sFett() {
		iSetzeKontrolle(TE_STYLE.FETT, format.wechsle(TE_STYLE.FETT));
	}

	public void sUnterstrichen() {
		iSetzeKontrolle(TE_STYLE.UNTERSTRICHEN, format.wechsle(TE_STYLE.UNTERSTRICHEN));
	}

	public void sKursiv() {
		iSetzeKontrolle(TE_STYLE.KURSIV, format.wechsle(TE_STYLE.KURSIV));
	}

	public void sUeberschrift() {
		iSetzeKontrolle(TE_STYLE.UEBERSCHRIFT, format.wechsle(TE_STYLE.UEBERSCHRIFT));
	}

//	public void sDurchgestrichen() {
//		iSetzeKontrolle(TE_STYLE.DURCHGESTRICHEN, format.sDurchgestrichen());
//	}

	// EVENTS

	public void eTaste(KeyListener k) {
		t.k_editor.eTaste(k);
	}

	public void eGeaendert(S_Zuhoerer<Object> z) {
		t.zuhoererPlus(E_GEAENDERT, z);
	}

	// GIB

	public String[] gAuswahl(Point auswahl) {
		String text=this.k_editor.swt().getText(auswahl.x, auswahl.y-1);
		StyleRange[] styles=this.k_editor.swt().getStyleRanges(auswahl.x, auswahl.y-auswahl.x);
		for(StyleRange sr : styles)
			sr.start-=auswahl.x;
		Gruppe2<String,String> erg=KonverterTF.zuDB(text, styles);
		return new String[] {erg.g1(), erg.g2()};
	}

	public SWT_TextEditor gEditor() {
		return this.k_editor;
	}

	public Gruppe2<String,String> gTextUndFormat() {
		StyledText st=t.k_editor.swt();
		return KonverterTF.zuDB(st.getText(), st.getStyleRanges());
	}

	public String gHTML() {
		StyledText st=t.k_editor.swt();
		return KonverterHtml.zuHTML(st.getText(), st.getStyleRanges());
	}

	// ERZEUGEN

	protected S_SWT_Komponente<?,? extends Composite> layout() {
		return this.k_editor;
	}

	protected void initStyle() {
//		this.k_editor.sText(ZEICHEN.DEUTSCH); //Zum testen
	}

	protected void initEvents() {
		this.f_verschieben.init(t.k_editor.swt());

		//Unsinn, aber keine bessere Lösung: CursorMoveEvent > Taste > MotifyEvent > altenStyleNehmen > Taste
		final Engpass<Boolean> carF=Engpass.neu(false);
		final Engpass<Boolean> carK=Engpass.neu(false);
		final Engpass<Boolean> carU=Engpass.neu(false);
		final Engpass<Boolean> carS=Engpass.neu(false);
//		final Engpass<Boolean> carD=Engpass.neu(false);
		final Engpass<Boolean> carH=Engpass.neu(false);

		t.k_editor.swt().addExtendedModifyListener(new ExtendedModifyListener() {
			public void modifyText(ExtendedModifyEvent event) {
				if(carF.g()) format.setze(TE_STYLE.FETT, 			true, new Point(event.start, event.length), false);
				if(carK.g()) format.setze(TE_STYLE.KURSIV, 			true, new Point(event.start, event.length), false);
				if(carU.g()) format.setze(TE_STYLE.UNTERSTRICHEN, 	true, new Point(event.start, event.length), false);
				if(carS.g()) format.setze(TE_STYLE.DURCHGESTRICHEN, true, new Point(event.start, event.length), false);
//				if(carD.g()) format.setze(TE_STYLE.DOPPELTUNTER, 	true, new Point(event.start, event.length), false);
				if(carH.g()) format.setze(TE_STYLE.UEBERSCHRIFT, 	true, new Point(event.start, event.length), false);

				t.kontrolle.gib(TE_STYLE.FETT).setze(carF.g());
				t.kontrolle.gib(TE_STYLE.KURSIV).setze(carK.g());
				t.kontrolle.gib(TE_STYLE.UNTERSTRICHEN).setze(carU.g());
				t.kontrolle.gib(TE_STYLE.DURCHGESTRICHEN).setze(carS.g());
//				t.kontrolle.gib(TE_STYLE.DOPPELTUNTER).setze(carD.g());
				t.kontrolle.gib(TE_STYLE.UEBERSCHRIFT).setze(carH.g());
				t.zurueck.plus();
				t.zuhoererStart(E_GEAENDERT);
	        }
		});

		t.k_editor.swt().addCaretListener(new CaretListener() { //Cursor bewegt
			public void caretMoved(CaretEvent event) {
				S_StyleKontrolle taste_f=t.kontrolle.gib(TE_STYLE.FETT);
				S_StyleKontrolle taste_k=t.kontrolle.gib(TE_STYLE.KURSIV);
				S_StyleKontrolle taste_u=t.kontrolle.gib(TE_STYLE.UNTERSTRICHEN);
				S_StyleKontrolle taste_s=t.kontrolle.gib(TE_STYLE.DURCHGESTRICHEN);
//				S_StyleKontrolle taste_d=t.kontrolle.gib(TE_STYLE.DOPPELTUNTER);
				S_StyleKontrolle taste_h=t.kontrolle.gib(TE_STYLE.UEBERSCHRIFT);

				carF.s(taste_f.gib());//, false));
				carK.s(taste_k.gib());
				carU.s(taste_u.gib());
				carS.s(taste_s.gib());
//				carD.s(taste_d.gib());
				carH.s(taste_h.gib());
//				Ausgabe.debug("Moved", carF.g());


				Point p=iStyleUebernahme(event.caretOffset, 1, true);
				if(p==null)
					return;

				t.kontrolle.gib(TE_STYLE.FETT)				.setze(MN.beiNull(format.gib(TE_STYLE.FETT, p), false));
				t.kontrolle.gib(TE_STYLE.KURSIV)			.setze(MN.beiNull(format.gib(TE_STYLE.KURSIV, p), false));
				t.kontrolle.gib(TE_STYLE.UNTERSTRICHEN)		.setze(MN.beiNull(format.gib(TE_STYLE.UNTERSTRICHEN, p), false));
				t.kontrolle.gib(TE_STYLE.DURCHGESTRICHEN)	.setze(MN.beiNull(format.gib(TE_STYLE.DURCHGESTRICHEN, p), false));
//				t.kontrolle.gib(TE_STYLE.DOPPELTUNTER)		.setze(MN.beiNull(format.gib(TE_STYLE.DOPPELTUNTER, p), false));
				t.kontrolle.gib(TE_STYLE.UEBERSCHRIFT)		.setze(MN.beiNull(format.gib(TE_STYLE.UEBERSCHRIFT, p), false));
			}
		});

		this.k_editor.eTaste(new KeyListener(){
			public void keyPressed(KeyEvent e) {
//				if(e.keyCode=='c' && e.stateMask==SWT.CTRL) {
//					Point auswahl=k_editor.swt().getSelection();
//					TypTabelle2<DNDTYP,Object> tt=TypTabelle2.neu(DNDTYP.class, Object.class);
//
//					String[] tf=t.gAuswahl(auswahl);
//					tt.plus(DNDTYP.MN_TEXTFORMAT, new TextFormatObjekt(tf[0], tf[1]));
//
//					String text= auswahl.x!=auswahl.y
//						? t.k_editor.swt().getText(auswahl.x, auswahl.y-1)
//						: t.k_editor.gText();
//					tt.plus(DNDTYP.TEXT, text);
//
//					SWT_Zwischenablage.setze(tt);
//					Ausgabe.debug(tt);
//				}
//
//				if(e.keyCode=='v' && e.stateMask==SWT.CTRL) {
//			    	Gruppe2<DNDTYP, Object> g=SWT_Zwischenablage.gib(DNDTYP.MN_TEXTFORMAT, DNDTYP.TEXT);
//			    	Ausgabe.debug(g.g1());
//
//			    	if(g.g1()==DNDTYP.MN_TEXTFORMAT) {
//			    		TextFormatObjekt tfo=(TextFormatObjekt)g.g2();
//			    		if(tfo==null)
//			    			return;
//			    		t.einfuegen(tfo.text, tfo.format);
//			    	}
//			    	else if(g.g1()==DNDTYP.TEXT)
//			    		k_editor.swt().insert(""+g.g2());
//				}
			}
			public void keyReleased(KeyEvent e) {
//				Ausgabe.debug((char)e.keyCode);
//				Ausgabe.debug(e.keyCode, e.character, e.data, TasteSWT.textZuZahl("STRG+U"));
				if(e.keyCode=='u' && e.stateMask==SWT.CTRL)
					sUnterstrichen();
				if((e.keyCode=='f' || e.keyCode=='b') && e.stateMask==SWT.CTRL)
					sFett();
				if((/*e.keyCode=='i' ||*/ e.keyCode=='k') && e.stateMask==SWT.CTRL) //i ist bereits per Standard belegt
					sKursiv();
				if((e.keyCode=='h') && e.stateMask==SWT.CTRL) //H ist auch Suchen und Ersetzen
					sUeberschrift();
//				if(e.keyCode==TasteSWT.textZuZahl("STRG+C")) {  //Funzt so nicht
//					Ausgabe.debug("C");
//					TypTabelle2<DND2,Object> tab=TypTabelle2.neu(DND2.class,Object.class);
//					tab.plus(DNDTYP.HTML, t.gHTML());
//					tab.plus(DNDTYP.TEXT, t.k_editor.gText());
//					Ausgabe.debug(tab);
//					SWT_Zwischenablage.setze(tab);
//				}
//				if(e.keyCode==TasteSWT.textZuZahl("STRG+V")) {
//					Ausgabe.debug("V");
//					Gruppe2<DND2,Object> drop=SWT_Zwischenablage.gib(new DND2[]{DNDTYP.HTML, DNDTYP.TEXT});
//					Ausgabe.debug(drop);
//					if(drop.g1()==DNDTYP.HTML) t.sHTML(""+drop.g2());
//					if(drop.g2()==DNDTYP.TEXT) t.k_editor.sText(""+drop.g2());
//				}

				if(e.keyCode=='o' && e.stateMask==SWT.CTRL) {
					Gruppe2<String,String> sf=t.gTextUndFormat();
					t.sTextUndFormat(sf.g1(), sf.g2());
				}

				if(e.keyCode=='z' && e.stateMask==SWT.CTRL)
					t.zurueck.zurueck();
				if(e.keyCode=='y' && e.stateMask==SWT.CTRL)
					t.zurueck.vor();

				f_verschieben.taste(e);
			}
		});

		t.format.eGeaendert(new S_Zuhoerer<Object>() {
			public void verarbeite(Object o) {
				t.zurueck.plus();
				t.zuhoererStart(E_GEAENDERT);
			}
		});

		this.f_zeilenHG.init(this.k_editor, zeilenHGFarbe);
		this.f_transfer.init(this, this.k_editor);

		t.zurueck.plus();
		t.k_editor.sFokus();
	}

	//------------------------------------------

//	public void sHTML(String text) {
//		t.zurueck.neu();
//		Gruppe2<String,StyleRange[]> g=KonverterHtml.vonHTML(text);
//		iSetze(g);
//	}

//	public void eGeaendert(S_Zuhoerer<?> z) {
//		this.zuhoererPlus(E_AENDERUNG, z);
//	}

	// INTERN

	private Point iStyleUebernahme(int start, int len, boolean cursor) {
		if(start==0) {
			if(t.k_editor.swt().getText().length()>=len+1) { //Style von rechts holen
				//TODO Einfügen am Anfang geht noch nicht richtig!
				start=cursor ? 0 : 1;
				len=1;
			}
			else return null;
		}
		else start-=1; //Style von links holen
		return new Point(start, len);
	}

	private void iSetzeKontrolle(TE_STYLE style, Boolean gesetzt) {
		if(gesetzt!=null)
			t.kontrolle.gib(style).setze(gesetzt);
	}

}
